/*
 * Copyright (c) 2018, apexes.net. All rights reserved.
 *
 *         http://www.apexes.net
 *
 */
package net.apexes.commons.lang;

import java.util.*;

/**
 * @author <a href="mailto:hedyn@foxmail.com">HeDYn</a>
 */
public class Comparators {

    /**
     * 比较 version1 和 version2，如果 version1 小于、等于、大于 version2 分别返回 -1、0、1
     * @param version1 版本号1
     * @param version2 版本号2
     * @return 如果 version1 小于、等于、大于 version2 分别返回 -1、0、1
     */
    public static int versionCompare(String version1, String version2) {
        if (version1 == null) {
            if (version2 == null) {
                return 0;
            } else {
                return -1;
            }
        } else if (version2 == null) {
            return 1;
        }
        StringTokenizer t1 = new StringTokenizer(version1, "._");
        StringTokenizer t2 = new StringTokenizer(version2, "._");
        while (t1.hasMoreTokens()) {
            if (!t2.hasMoreTokens()) {
                return 1;
            }
            int n1 = Integer.parseInt(t1.nextToken());
            int n2 = Integer.parseInt(t2.nextToken());
            int d = n1 - n2;
            if (d != 0) {
                return d;
            }
        }
        return t2.hasMoreTokens() ? -1 : 0;
    }

    /**
     * 添加参与对比的属性
     * @param getter 获取属性值的方法
     * @param comparator 用于给属性做对比
     * @return 返回 {@link ComparatorHelper} 对象
     */
    public static <E, R> ComparatorHelper<E> orderBy(ObjectGetter<E, R> getter, Comparator<R> comparator) {
        return new ComparatorHelper<E>().orderBy(getter, comparator);
    }

    /**
     * 参与对比的属性
     * @param getter 获取要排序的属性值的方法
     * @return 返回 {@link ComparatorHelper} 对象
     */
    public static <E> ComparatorHelper<E> orderBy(Getter<E> getter) {
        return new ComparatorHelper<E>().orderBy(getter);
    }

    /**
     * 参与对比的属性
     * @param getter 获取要排序的属性值的方法
     * @param asc 为true使用升序，为false使用降序
     * @return 返回 {@link ComparatorHelper} 对象
     */
    public static <E> ComparatorHelper<E> orderBy(Getter<E> getter, boolean asc) {
        return new ComparatorHelper<E>().orderBy(getter, asc);
    }

    /**
     * 参与对比的属性
     * @param getter 获取要排序的属性值的方法
     * @return 返回 {@link ComparatorHelper} 对象
     */
    public static <E> ComparatorHelper<E> ascBy(Getter<E> getter) {
        return orderBy(getter, true);
    }

    /**
     * 参与对比的属性
     * @param getter 获取要排序的属性值的方法
     * @return 返回 {@link ComparatorHelper} 对象
     */
    public static <E> ComparatorHelper<E> descBy(Getter<E> getter) {
        return orderBy(getter, false);
    }

    /**
     * 对列表进行升序排序，为null的排在前面
     * @param list 要排序的列表
     * @param getter 获取要排序的值的方法
     */
    public static <E> void sort(List<E> list, Getter<E> getter) {
        orderBy(getter).sort(list);
    }

    /**
     * 对列表进行排序，为null的排在前面
     * @param list 要排序的列表
     * @param getter 获取要排序的值的方法
     * @param asc 为true使用升序，为false使用降序
     */
    public static <E> void sort(List<E> list, Getter<E> getter, boolean asc) {
        orderBy(getter, asc).sort(list);
    }

    /**
     * 对列表进行排序，为null排在后面
     * @param list 要排序的列表
     * @param getter 获取要排序的值的方法
     */
    public static <E> void sortNullLast(List<E> list, Getter<E> getter) {
        orderBy(getter).nullLast().sort(list);
    }

    /**
     * 对列表进行排序，为null排在后面
     * @param list 要排序的列表
     * @param getter 获取要排序的值的方法
     * @param asc 为true使用升序，为false使用降序
     */
    public static <E> void sortNullLast(List<E> list, Getter<E> getter, boolean asc) {
        orderBy(getter, asc).nullLast().sort(list);
    }

    /**
     * 对列表进行升序排序，为null排在前面
     * @param list 要排序的列表
     * @param getter 获取要排序的值的方法
     */
    public static <E> void ascSort(List<E> list, Getter<E> getter) {
        sort(list, getter, true);
    }

    /**
     * 对列表进行降序排序，为null排在前面
     * @param list 要排序的列表
     * @param getter 获取要排序的值的方法
     */
    public static <E> void descSort(List<E> list, Getter<E> getter) {
        sort(list, getter, false);
    }

    /**
     * 对列表进行升序排序，为null排在后面
     * @param list 要排序的列表
     * @param getter 获取要排序的值的方法
     */
    public static <E> void ascSortNullLast(List<E> list, Getter<E> getter) {
        sortNullLast(list, getter, true);
    }

    /**
     * 对列表进行降序排序，为null排在后面
     * @param list 要排序的列表
     * @param getter 获取要排序的值的方法
     */
    public static <E> void descSortNullLast(List<E> list, Getter<E> getter) {
        sortNullLast(list, getter, false);
    }

    /**
     * 对比两个值，空值排在前面
     * @param o1 值1
     * @param o2 值2
     * @return 非空的值1 小于、等于、大于 非空的值2 时分别返回 -1、0、1
     */
    public static <E extends Comparable<E>> int compare(E o1, E o2) {
        return compareNull(o1, o2, false);
    }

    /**
     * 对比两个值，空值排在前面
     * @param o1 值1
     * @param o2 值2
     * @return 非空的值1 小于、等于、大于 非空的值2 时分别返回 -1、0、1
     */
    public static <E extends Comparable<E>> int nullFirstCompare(E o1, E o2) {
        return compareNull(o1, o2, false);
    }

    /**
     * 对比两个值，空值排在后面
     * @param o1 值1
     * @param o2 值2
     * @return 非空的值1 小于、等于、大于 非空的值2 时分别返回 -1、0、1
     */
    public static <E extends Comparable<E>> int nullLastCompare(E o1, E o2) {
        return compareNull(o1, o2, true);
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    static int compareNull(Comparable o1, Comparable o2, boolean nullLast) {
        int v = xorCompare(o1, o2, nullLast);
        if (v == 0 && o1 != null && o2 != null) {
            v = o1.compareTo(o2);
        }
        if (v == 0) {
            return 0;
        }
        return v < 0 ? -1 : 1;
    }

    static <E> int xorCompare(E o1, E o2, boolean nullLast) {
        if (o1 == null) {
            if (o2 != null) {
                return nullLast ? 1 : -1;
            }
        } else if (o2 == null) {
            return nullLast ? -1 : 1;
        }
        return 0;
    }

    /**
     * 包装一个 null 值排在前面的 {@link Comparator} 对象
     * @param comparator 原 {@link Comparator} 对象
     * @return 返回一个 null 值排在前面的 {@link Comparator} 对象
     */
    public static <E> Comparator<E> nullFirstComparator(Comparator<E> comparator) {
        return nullableComparator(comparator, false);
    }

    /**
     * 包装一个 null 值排在后面的 {@link Comparator} 对象
     * @param comparator 原 {@link Comparator} 对象
     * @return 返回一个 null 值排在后面的 {@link Comparator} 对象
     */
    public static <E> Comparator<E> nullLastComparator(Comparator<E> comparator) {
        return nullableComparator(comparator, true);
    }

    /**
     * 包装一个支持 null 的 {@link Comparator} 对象
     * @param comparator 原 {@link Comparator} 对象
     * @param nullLast 为 true 时 null 排在后面，否则 null 排在前面
     * @return 返回一个 null 值排在后面的 {@link Comparator} 对象
     */
    public static <E> Comparator<E> nullableComparator(Comparator<E> comparator, boolean nullLast) {
        return new NullableComparator<>(comparator, nullLast);
    }

    private static class NullableComparator<E> implements Comparator<E> {

        private final Comparator<E> comparator;
        private final boolean nullLast;

        NullableComparator(Comparator<E> comparator, boolean nullLast) {
            this.comparator = comparator;
            this.nullLast = nullLast;
        }

        @Override
        public int compare(E o1, E o2) {
            int v = xorCompare(o1, o2, nullLast);
            if (v == 0 && o1 != null && o2 != null) {
                v = comparator.compare(o1, o2);
            }
            if (v == 0) {
                return 0;
            }
            return v < 0 ? -1 : 1;
        }
    }

    /**
     *
     * @author <a href="mailto:hedyn@foxmail.com">HeDYn</a>
     *
     * @param <E>
     */
    public static class ComparatorHelper<E> implements Comparator<E> {

        private final List<Comparator<E>> comparators;
        private boolean nullLast = false;

        private ComparatorHelper() {
            comparators = new ArrayList<>();
        }

        /**
         * 添加参与对比的属性
         * @param getter 获取属性值的方法
         * @param comparator 用于给属性做对比
         * @return 返回 {@link ComparatorHelper} 对象
         */
        public <R> ComparatorHelper<E> orderBy(ObjectGetter<E, R> getter, Comparator<R> comparator) {
            comparators.add(new ObjectGetterComparator<>(this, getter, comparator));
            return this;
        }

        /**
         * 添加参与对比的属性，该属性升序
         * @param getter 获取属性值的方法
         * @return 返回 {@link ComparatorHelper} 对象
         */
        public ComparatorHelper<E> orderBy(Getter<E> getter) {
            comparators.add(new ComparableGetterComparator<>(this, getter));
            return this;
        }

        /**
         * 添加参与对比的属性
         * @param getter 获取属性值的方法
         * @param asc 为true使用升序，为false使用降序
         * @return 返回 {@link ComparatorHelper} 对象
         */
        public ComparatorHelper<E> orderBy(Getter<E> getter, boolean asc) {
            comparators.add(new ComparableGetterComparator<>(this, getter, asc));
            return this;
        }

        /**
         * 添加参与对比的属性，该属性升序
         * @param getter 获取属性值的方法
         * @return 返回 {@link ComparatorHelper} 对象
         */
        public ComparatorHelper<E> ascBy(Getter<E> getter) {
            return orderBy(getter, true);
        }

        /**
         * 添加参与对比的属性，该属性倒序
         * @param getter 获取属性值的方法
         * @return 返回 {@link ComparatorHelper} 对象
         */
        public ComparatorHelper<E> descBy(Getter<E> getter) {
            return orderBy(getter, false);
        }

        /**
         * 为null排在后面
         * @return 返回 {@link ComparatorHelper} 对象
         */
        public ComparatorHelper<E> nullLast() {
            nullLast = true;
            return this;
        }

        /**
         * 对指定的列表进行排序
         * @param list 要排序的列表
         */
        public void sort(List<E> list) {
            list.sort(this);
        }

        @Override
        public int compare(E o1, E o2) {
            int v = Comparators.xorCompare(o1, o2, nullLast);
            if (v == 0) {
                for (Comparator<E> comparator : comparators) {
                    v = comparator.compare(o1, o2);
                    if (v != 0) {
                        break;
                    }
                }
            }
            return v;
        }

    }
    
    private static class ComparableGetterComparator<E> implements Comparator<E> {

        private final ComparatorHelper<E> helper;
        private final Getter<E> getter;
        private final boolean asc;

        ComparableGetterComparator(ComparatorHelper<E> helper, Getter<E> getter) {
            this(helper, getter, true);
        }

        ComparableGetterComparator(ComparatorHelper<E> helper, Getter<E> getter, boolean asc) {
            this.helper = helper;
            this.getter = getter;
            this.asc = asc;
        }

        @Override
        public int compare(E o1, E o2) {
            Comparable<?> c1 = getter.apply(o1);
            Comparable<?> c2 = getter.apply(o2);
            int v = Comparators.compareNull(c1, c2, helper.nullLast);
            if (asc) {
                return v;
            } else {
                return -v;
            }
        }
    }

    private static class ObjectGetterComparator<E, R> implements Comparator<E> {

        private final ComparatorHelper<E> helper;
        private final ObjectGetter<E, R> getter;
        private final Comparator<R> comparator;

        ObjectGetterComparator(ComparatorHelper<E> helper, ObjectGetter<E, R> getter, Comparator<R> comparator) {
            this.helper = helper;
            this.getter = getter;
            this.comparator = comparator;
        }

        @Override
        public int compare(E o1, E o2) {
            return compareNull(getter.apply(o1), getter.apply(o2));
        }

        private int compareNull(R o1, R o2) {
            int v = xorCompare(o1, o2, helper.nullLast);
            if (v == 0 && o1 != null && o2 != null) {
                v = comparator.compare(o1, o2);
            }
            if (v == 0) {
                return 0;
            }
            return v < 0 ? -1 : 1;
        }
    }

    /**
     *
     * @param <E>
     */
    public interface Getter<E> {

        Comparable<?> apply(E o);

    }

    /**
     *
     * @param <E>
     * @param <R>
     */
    public interface ObjectGetter<E, R> {

        R apply(E o);

    }
}
